' Strict
'SuperStrict

' Modules
'Import BRL.Max2D

' Files
Include "TBox.bmx"

' TWidget
Type TWidget Abstract
	Field list:TList
	Field link:TLink
	Field childs:TMap
	
	Field listReversed:TList
	
	Field id:String
	Field text:String
	
	Field cAbs:TBox
	Field cRel:TBoxFloat
	
	Field alpha:Float
	Field r:Byte, g:Byte, b:Byte
	Field textR:Byte, textG:Byte, textB:Byte
	Field visible:Int
	Field font:TImageFont
	
	Field padding:TBox
	Field skinPadding:TBox
	
	Field popupMenu:TPopupMenu
	
	' Parent
	Field gui:TGUI
	Field parent:TWidget
	
	' State
	Field hovered:Int
	
	' "Real" coordinates (padding, but still relative to the parent)
	Field rX:Int
	Field rY:Int
	Field rX2:Int
	Field rY2:Int
	Field rWidth:Int
	Field rHeight:Int
	
	' Screen coordinates
	Field screenX:Int
	Field screenY:Int
	
	' Event handlers
	Field affectingWidget:TWidget
	Field onHover(widget:TWidget)
	Field onClick(widget:TWidget)
	
	' InitWidget
	Method InitWidget(nID:String, nText:String, nX:Int = 0, nY:Int = 0, nWidth:Int = 0, nHeight:Int = 0)
		Self.list = CreateList()
		Self.listReversed = CreateList() 
		Self.childs = CreateMap()
		Self.id = nID
		
		Self.cAbs = TBox.Create()
		Self.cRel = TBoxFloat.Create()
		Self.padding = TBox.Create() 
		Self.skinPadding = TBox.Create()
		
		Self.SetPosition(0, 0)
		Self.SetPositionAbs(nX, nY)
		Self.SetSize(0, 0)
		Self.SetSizeAbs(nWidth, nHeight)
		
		Self.SetColor(255, 255, 255)
		Self.SetAlpha(1.0)
		Self.SetTextColor(0, 0, 0)
		Self.SetPadding(0, 0, 0, 0)
		Self.SetText(nText) 
		Self.SetVisible(True) 
		Self.SetAffectingWidget(Self) 
	End Method
	
	' SetPosition
	Method SetPosition(nX1:Float, nY1:Float) 
		' x2 and y2 will be set initially in SetSize and SetSizeAbs
		Self.cRel.x2 :+ nX1 - Self.cRel.x1
		Self.cRel.y2 :+ nY1 - Self.cRel.y1
		Self.cRel.x1 = nX1
		Self.cRel.y1 = nY1
		
		Self.UpdatePosition()
	End Method
	
	' SetPositionAbs
	Method SetPositionAbs(nX1:Int, nY1:Int) 
		' x2 and y2 will be set initially in SetSize and SetSizeAbs
		Self.cAbs.x2 :+ nX1 - Self.cAbs.x1
		Self.cAbs.y2 :+ nY1 - Self.cAbs.y1
		Self.cAbs.x1 = nX1
		Self.cAbs.y1 = nY1
		
		Self.UpdatePosition()
	End Method
	
	' SetSize
	Method SetSize(nWidth:Float = -1.0, nHeight:Float = -1.0)
		If nWidth <> -1.0
			Self.cRel.x2 = Self.cRel.x1 + nWidth
		EndIf
		
		If nHeight <> -1.0
			Self.cRel.y2 = Self.cRel.y1 + nHeight
		EndIf
		
		Self.UpdatePosition()
	End Method
	
	' SetSizeAbs
	Method SetSizeAbs(nWidth:Int, nHeight:Int)
		If nWidth <> -1
			Self.cAbs.x2 = Self.cAbs.x1 + nWidth
		EndIf
		
		If nHeight <> -1
			Self.cAbs.y2 = Self.cAbs.y1 + nHeight
		EndIf
		
		Self.UpdatePosition()
	End Method
	
	' SetColor
	Method SetColor(nR:Int, nG:Int, nB:Int)
		Self.r = nR
		Self.g = nG
		Self.b = nB
	End Method
	
	' SetTextColor
	Method SetTextColor(nR:Int, nG:Int, nB:Int)
		Self.textR = nR
		Self.textG = nG
		Self.textB = nB
	End Method
	
	' SetAlpha
	Method SetAlpha(nAlpha:Float)
		Self.alpha = nAlpha
	End Method
	
	' SetFont
	Method SetFont(nFont:TImageFont)
		Self.font = nFont
	End Method
	
	' SetFontAll
	Method SetFontAll(nFont:TImageFont)
		Self.font = nFont
		For Local widget:TWidget = EachIn Self.list
			widget.SetFontAll(nFont)
		Next
	End Method
	
	' SetPadding
	Method SetPadding(nPaddingLeft:Int, nPaddingTop:Int, nPaddingRight:Int, nPaddingBottom:Int)
		Self.padding.x1 = nPaddingLeft
		Self.padding.y1 = nPaddingTop
		Self.padding.x2 = nPaddingRight
		Self.padding.y2 = nPaddingBottom
	End Method
	
	' SetVisible
	Method SetVisible(show:Int) 
		Self.visible = show
	End Method
	
	' IsVisible
	Method IsVisible:Int()
		Return Self.visible
	End Method
	
	' SetAffectingWidget
	Method SetAffectingWidget(widget:TWidget)
		Self.affectingWidget = widget
	End Method
	
	' GetAffectingWidget
	Method GetAffectingWidget:TWidget()
		Return Self.affectingWidget
	End Method
	
	' SetPopupMenu
	Method SetPopupMenu(menu:TPopupMenu) 
		Self.popupMenu = menu
	End Method
	
	' GetPopupMenu
	Method GetPopupMenu:TPopupMenu() 
		Return Self.popupMenu
	End Method
	
	' UpdatePosition
	Method UpdatePosition()
		' TODO: Update
		If parent <> Null
			Self.rX = Self.cAbs.x1 + Self.cRel.x1 * (Self.parent.rWidth - Self.parent.skinPadding.x1 - Self.parent.skinPadding.x2 - Self.parent.padding.x1 - Self.parent.padding.x2)
			Self.rY = Self.cAbs.y1 + Self.cRel.y1 * (Self.parent.rHeight - Self.parent.skinPadding.y1 - Self.parent.skinPadding.y2 - Self.parent.padding.y1 - Self.parent.padding.y2)
			Self.rX2 = Self.cAbs.x2 + Self.cRel.x2 * (Self.parent.rWidth - Self.parent.skinPadding.x1 - Self.parent.skinPadding.x2 - Self.parent.padding.x1 - Self.parent.padding.x2)
			Self.rY2 = Self.cAbs.y2 + Self.cRel.y2 * (Self.parent.rHeight - Self.parent.skinPadding.y1 - Self.parent.skinPadding.y2 - Self.parent.padding.y1 - Self.parent.padding.y2) 
		Else
			Self.rX = Self.cAbs.x1 + Self.cRel.x1 * GraphicsWidth()
			Self.rY = Self.cAbs.y1 + Self.cRel.y1 * GraphicsHeight()
			Self.rX2 = Self.cAbs.x2 + Self.cRel.x2 * GraphicsWidth()
			Self.rY2 = Self.cAbs.y2 + Self.cRel.y2 * GraphicsHeight()
		EndIf
		
		Self.rWidth = Self.rX2 - Self.rX
		Self.rHeight = Self.rY2 - Self.rY
		
		Self.UpdateScreenPosition()
		
		'DrawRect Self.rX1, Self.rY1, Self.rX2 - Self.rX, Self.rY2 - Self.rY
	End Method
	
	' UpdateScreenPosition
	Method UpdateScreenPosition()
		If parent <> Null
			Self.screenX = Self.parent.screenX + Self.parent.skinPadding.x1 + Self.parent.padding.x1 + Self.rX
			Self.screenY = Self.parent.screenY + Self.parent.skinPadding.y1 + Self.parent.padding.y1 + Self.rY
		Else
			Self.screenX = Self.rX
			Self.screenY = Self.rY
		EndIf
		
		' Update childs
		For Local widget:TWidget = EachIn Self.list
			widget.UpdateScreenPosition()
		Next
	End Method
	
	' GetRelOffsetX
	Method GetRelOffsetX:Int()
		Return Self.rX - Self.cAbs.x1
	End Method
	
	' GetRelOffsetY
	Method GetRelOffsetY:Int()
		Return Self.rY - Self.cAbs.y1
	End Method
	
	' MoveTo
	Method MoveTo(toX:Int, toY:Int)
		' Moves the window to the specified position, ignoring relative positioning
		Self.SetPositionAbs(toX - Self.GetRelOffsetX(), toY - Self.GetRelOffsetY())
	End Method
	
	' DrawAll
	Method DrawAll() 
		If Self.visible = False
			Return
		EndIf
		
		ResetMax2D()
		Self.Draw()
		For Local widget:TWidget = EachIn Self.list
			ResetMax2D()
			.SetOrigin Self.screenX + Self.skinPadding.x1 + Self.padding.x1, Self.screenY + Self.skinPadding.y1 + Self.padding.y1
			'.SetViewport Self.rX + Self.padding.x1, Self.rY + Self.padding.y1, Self.padding.x2 - Self.padding.x1, Self.padding.y2 - Self.padding.y1
			widget.DrawAll()
		Next
		Self.DrawEnd()
		.SetOrigin 0, 0
	End Method
	
	' UpdateAll
	Method UpdateAll()
		If Self.visible = False
			Return
		EndIf
		
		For Local widget:TWidget = EachIn Self.listReversed
			widget.UpdateAll()
		Next
		Self.Update()
	End Method
	
	' GetID
	Method GetID:String()
		Return Self.id
	End Method
	
	' GetScreenX
	Method GetScreenX:Int()
		Return Self.screenX
	End Method
	
	' GetScreenY
	Method GetScreenY:Int()
		Return Self.screenY
	End Method
	
	' GetScreenX2
	Method GetScreenX2:Int()
		Return Self.screenX + Self.rWidth
	End Method
	
	' GetScreenY2
	Method GetScreenY2:Int()
		Return Self.screenY + Self.rHeight
	End Method
	
	' GetChild
	Method GetChild:TWidget(id:String)
		Return TWidget(Self.childs.ValueForKey(id))
	End Method
	
	' InitSkinPadding
	Method InitSkinPadding() 
		' override this method
	End Method
	
	' Add
	Method Add(widget:TWidget)
		widget.link = Self.list.AddLast(widget)
		widget.parent = Self
		widget.gui = Self.gui
		Self.listReversed.AddFirst(widget)
		Self.childs.Insert(widget.GetID(), widget) 
		
		If Self.gui <> Null And Self <> Self.gui.root And widget.popupMenu <> Null And widget.popupMenu.parent = Null
			Self.gui.AddMenu(widget.popupMenu) 
		EndIf
		
		' Widget childs
		widget.OnGUIAvailable() 
		
		' Due to "late skin binding" padding has to be updated
		widget.InitSkinPadding() 
		widget.UpdatePosition()
		
		Self.UpdateScreenPosition()
	End Method
	
	' Remove
	Method Remove()
		Self.link.Remove()
		
		' Null
		Self.affectingWidget = Null
		
		' TODO: Optimize
		If Self.parent <> Null
			Self.parent.listReversed = Self.parent.list.Reversed() 
			Self.parent.childs.Remove(Self.GetID())
		EndIf
	End Method
	
	' Focus
	Method Focus()
		gui.newFocusWidget = Self
		TGUI.logger.Write("Focus: " + Self.text)
	End Method
	
	' HasFocus
	Method HasFocus:Int()
		Return gui.focusWidget = Self
	End Method
	
	' ApplyLayoutTable
	Method ApplyLayoutTable(rows:Int, columns:Int = 1)
		Local cellWidth:Int = Self.rWidth / columns
		Local cellHeight:Int = Self.rHeight / rows
		Local iterX:Int = 0
		Local iterY:Int = 0
		
		For Local widget:TWidget = EachIn Self.list
			widget.SetPositionAbs(iterX * cellWidth, iterY * cellHeight)
			widget.SetSizeAbs(cellWidth, cellHeight)
			iterX :+ 1
			If iterX >= columns
				iterX = 0
				iterY :+ 1
			EndIf
		Next
	End Method
	
	' PrintDebug
	Method PrintDebug()
		If Self.parent <> Null
			Print Self.id + ".parent: " + Self.parent.id
		EndIf
		Print Self.id + ".visible: " + Self.IsVisible()
		Print Self.id + ".cAbs: " + Self.cAbs.ToString()
		Print Self.id + ".cRel: " + Self.cRel.ToString() 
		Print Self.id + ".rPos: " + Self.rX + ", " + Self.rY + ", " + Self.rWidth + ", " + Self.rHeight
		Print Self.id + ".screenPos: " + Self.screenX + ", " + Self.screenY
		Print Self.id + ".padding: " + Self.padding.ToString()
		Print Self.id + ".skinPadding: " + Self.skinPadding.ToString()
	End Method
	
	' OnGUIAvailable
	Method OnGUIAvailable() 
		
	End Method
	
	' Update
	Method Update() Abstract
	
	' Draw
	Method Draw() Abstract
	
	' DrawEnd
	Method DrawEnd()
		
	End Method
	
	' SetText
	Method SetText(nText:String) Abstract
End Type
